-
Notifications
You must be signed in to change notification settings - Fork 7.7k
[compiler] Rewrite React Compiler Docs #7868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Size changes📦 Next.js Bundle Analysis for react-devThis analysis was generated by the Next.js Bundle Analysis action. 🤖 Five Pages Changed SizeThe following pages changed size from the code in this PR compared to its base branch:
DetailsOnly the gzipped size is provided here based on an expert tip. First Load is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If Any third party scripts you have added directly to your app using the Next to the size is how much the size has increased or decreased compared with the base branch of this PR. If this percentage has increased by 10% or more, there will be a red status indicator applied, indicating that special attention should be given to this. |
@@ -12,6 +12,12 @@ const cachedValue = useMemo(calculateValue, dependencies) | |||
|
|||
</Intro> | |||
|
|||
<Note> | |||
|
|||
[React Compiler](/learn/react-compiler) automatically memoizes values and functions, reducing the need for manual `useMemo` calls. You can use the compiler to handle memoization automatically. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is great, was just browsing the docs and surprised it wasn't there
import { isCompilerEnabled } from 'ReactCompilerFeatureFlag'; | ||
import { c as _c } from 'react/compiler-runtime'; | ||
|
||
const MyComponent = isCompilerEnabled() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If that's implemented at a component level and doesn't utilize dynamic imports, can we expect a big increase in bundle size?
As effectively, we are importing double the amount of code per optimized component?
I know it won't be exactly a 2x increase, as we aren't optimizing node_modules nor normal functions, but I wonder if this would negatively impact production in a significant way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the number of lines per function does get doubled, but the bundle size increase isn't 1:1 with the number of lines since it depends on the compression algorithm. For us, we found that a React Native (Android) project saw an overall increase of ~10% in apk size.
There's some tradeoff math here to do, while bundle size may increase in the gating mode, you will likely see performance benefits in the test
case that tradeoff against the bundle size. You'll need to run an experiment to really be sure.
Of course the gating mode is optional too. The Incremental Adoption page covers a bit more about other strategies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thanks @poteto!
61ba325
to
c8c6b3a
Compare
|
||
One of the main ways React Compiler can break your app is if your code was written to rely on memoization for correctness. This means your app depends on specific values being memoized to work properly. Since the compiler may memoize differently than your manual approach, this can lead to unexpected behavior like effects over-firing, infinite loops, or missing updates. | ||
|
||
Common scenarios where this occurs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another breaking pattern is when you reference a variable declaration inside a callback that was declared after the callback. This can happen accidentally when writing an especially complex component or hook. That callback won't be memoized by the compiler.
Would you consider this a common scenario?
https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAQgIYAmAFAJRHAA6xRchYORhAwgDZ5wBrIgF4itEQD56TIrJZsIPBADoeEAOZU0ECDRlEAvvv2tM7IgG1tEADREwCHADEdAXRFEoDgMo4yOBCoABj1MfRhHWGIAHgo8ADdOTF5+AWFgbj5BAwlgawNogHo4+IkmIzDMDGx8QiIAcR1qOkZmU3MrHTsHZzcPLwRff0CQkzYOTNSPcWEpVrl5M0UVNU1rUNkK8MiYGJKklMF0yezc-KKSsswDEAMgA
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this is a fun case. Note that you can't memoize such callbacks yourself — adding a useCallback()
in the "Bad" example in your playground link would be a TDZ violation when you added the dep on foo
:
function Bad() {
const onClick = useCallback(() => {
console.log(foo)
}, [foo /* TDZ violation */ ]);
const [foo, setFoo] = useState(0)
return <div onClick={onClick}>{foo}</div>
}
We can look into adding linting against this pattern to suggest moving the callback. Note that the compiler will already reject this code if you try to manually memoize it, because the dependency changes later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you consider this a common scenario?
Since this pattern doesn't work with manual memoization, this pattern is most likely in code that isn't memoized already. Definitely worth adding validation to help identify opportunities in such code to memoize more.
plugins: [ | ||
['babel-plugin-react-compiler', { | ||
compilationMode: 'annotation', // Only compile "use memo" components | ||
panicThreshold: 'NONE' // More permissive for experimental code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
panicThreshold: 'NONE' // More permissive for experimental code | |
panicThreshold: 'none' // More permissive for experimental code |
lowercased to match the types and be more consistent with src/content/reference/react-compiler/panicThreshold.md
test: './src/production/**/*.{js,jsx,ts,tsx}', | ||
plugins: [ | ||
['babel-plugin-react-compiler', { | ||
panicThreshold: 'CRITICAL_ERRORS' // Stricter for production code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
panicThreshold: 'CRITICAL_ERRORS' // Stricter for production code | |
panicThreshold: 'critical_errors' // Stricter for production code |
We've received [feedback](https://bsky.app/profile/danabra.mov/post/3lr46ciujjs2r) that the compiler docs are difficult to understand and not prominent enough that people don't realize the compiler is a serious project and is near stable. This PR rewrites the whole compiler doc section, giving it its own category as well as a standalone reference page. Preview: https://react-dev-git-pr7868-fbopensource.vercel.app/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Question:
It may not belong to the "incremental adoption" section, but I wonder if it's worth answering the following question in the adoption plan or somewhere else:
There is a recurring question regarding the best practice for handling existing manual memoization APIs (useMemo, useCallback, memo) in a codebase after the React Compiler has been enabled. This uncertainty is common in discussions about adopting the compiler for large, legacy applications.
To address this, we could add a section to the official documentation, that answers the following:
FAQ: What is the recommended strategy for useMemo, useCallback, and memo in a large and legacy codebase after enabling the React Compiler?
Once the compiler is active, should teams begin a gradual process of removing these manual memoizations?
Our working assumption is that this is the intended outcome, as it aligns with the core purpose of the compiler.
Providing clear, official guidance on this topic will significantly reduce community uncertainty and help teams establish a consistent and effective adoption plan.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Question:
Is the use of react-compiler-healthcheck
recommended at all?
We've received feedback that the compiler docs are difficult to understand and not prominent enough that people don't realize the compiler is a serious project and is near stable.
This PR rewrites the whole compiler doc section, giving it its own category as well as a standalone reference page.
Preview: https://react-dev-git-pr7868-fbopensource.vercel.app/